// 定义绘制函数
window.drawFlowImg = function ( flowCanvasFn ) {

	for ( var i in drawFn ) {
		this[i] = drawFn[i];
	}
	for ( var i in flowCanvasFn ) {
		this[i] = flowCanvasFn[i];
	}
}

window.drawFn = {
    drawFlowImage : function ( ) {
    	
    	// 测试用的node,非测试时要注释掉
//    	this.initTestNode();

	    // 绘制节点
	    this.drawNode ( );

	    // 绘制线条
	    this.drawLine ( );
	    
	    // 绘制驳回线条 , 在支持虚线的浏览器中才绘制驳回线条
	    if(this.cxt.setLineDash){
	    	this.drawBohui ( );
	    }

    } ,
    // 节点点击事件
    clickNode : function ( params ) {
	    var node = params.node;
	    var opinion = params.opinion;
	    var subInst = params.subInst;
	    var flowFn = params.fn;
	    var type = params.type;

	    if ( node ) {
		    this.imageInfo.selectNode = node;
	    }

	    // 绘制多实例
	    flowFn.drawMutiInstFlow ( node , opinion , type );

	    // 打开外部子流程
	    flowFn.openCallActivity ( node , subInst , type );

	    // 打开动态任务节点
	    flowFn.drawDynamicNodeFlow ( node );

    } ,
    drawDynamicNodeFlow : function ( node ) {
	    var dynamicTaskCounts = node.dynamicTaskCounts || "";
	    dynamicTaskCounts = dynamicTaskCounts + "";
	    if ( !node || !dynamicTaskCounts ) {
		    return;
	    }

	    // 获取意见列表
	    var opinions = node.opinions;
	    if ( !opinions ) {
		    return;
	    }
	    var nodeId = node.id;

	    var opJson = {};
	    var idx = 0;
	    for ( var i = 0 ; i < opinions.length ; i++ ) {
		    var oldOp = opJson[opinions[i].taskName] || {
			    opIdx : idx++
		    };
		    var oldIdx = oldOp.opIdx;
		    opJson[opinions[i].taskName] = opinions[i];
		    opJson[opinions[i].taskName].opIdx = oldIdx;
	    }
	    var opinionMap = {};
	    for ( var i in opJson ) {
		    var op = opJson[i];
		    opinionMap[nodeId + '_' + op.opIdx] = [ op ];
	    }

	    var dynamicTaskCountsSplit = dynamicTaskCounts.split ( "/" );
	    var count = parseInt ( dynamicTaskCountsSplit[dynamicTaskCountsSplit.length - 1] );

	    var startX = 100;
	    var startY = 100;
	    var lineWidth = 80;
	    var width = 100;
	    var height = 80;

	    var newNodeMap = {};
	    var newFlowLocation = {};
	    for ( var i = 0 ; i < count ; i++ ) {

		    // 创建节点
		    var id = nodeId + '_' + i;
		    var op = ( opinionMap[id] && opinionMap[id][0] ) || {};
		    var tempNode = {
		        id : id ,
		        height : height ,
		        width : width ,
		        x : startX + width * i + lineWidth * i ,
		        y : startY ,
		        taskName : op.taskName || node.nodeName ,
		        op : op
		    };
		    newNodeMap[id] = tempNode;

		    // 创建连线
		    if ( i < count - 1 ) {
			    id = nodeId + '_line_' + i;
			    var tempLine = [ {
			        id : id ,
			        height : 0 ,
			        width : 0 ,
			        x : startX + width * i + lineWidth * i + width ,
			        y : startY + height / 2 ,
			    } , {
			        id : id ,
			        height : 0 ,
			        width : 0 ,
			        x : startX + width * i + lineWidth * i + width + lineWidth ,
			        y : startY + height / 2 ,
			    } ];
			    newFlowLocation[id] = tempLine;
		    }
	    }
	    var newNodeList = [ ];
	    var newFlowLocationList = [ ];
	    for ( var i in newNodeMap ) {
		    newNodeList.push ( newNodeMap[i] );
	    }
	    for ( var i in newFlowLocation ) {
		    newFlowLocationList.push ( newFlowLocation[i] );
	    }

	    // 创建flowElements和stacks
	    var flowElements = [ ];
	    var stacks = [ ];
	    for ( var i = 0 ; i < newNodeList.length ; i++ ) {
		    var node = newNodeList[i];
		    var tempNodeFlowElements = {
		        id : node.id ,
		        name : node.taskName ,
		        status : node.op.status || 'default' ,
		        name : node.taskName ,
		        type : 'UserTask' ,
		    }
		    flowElements.push ( tempNodeFlowElements );
		    stacks.push ( {
		        actionName : node.op.status || 'default' ,
		        nodeId : node.id ,
		        nodeType : 'userTask' ,
		        nodeName : node.taskName ,
		    } );

		    if ( i < newNodeList.length - 1 ) {
			    var line = newFlowLocationList[i][0];
			    var tempLineFlowElements = {
			        id : line.id ,
			        sourceRef : newNodeList[i].id ,
			        targetRef : newNodeList[i + 1].id ,
			        status : node.op.status || 'default' ,
			        type : "SequenceFlow" ,
			    }
			    flowElements.push ( tempLineFlowElements );
			    stacks.push ( {
			        actionName : node.op.status || 'default' ,
			        nodeId : line.id ,
			        nodeType : 'sequenceFlow' ,
			    } );
			    tempNodeFlowElements.outgoingFlows = [ tempLineFlowElements ];
		    }
	    }

	    var imageInfo = {};

	    imageInfo.nodeMap = newNodeMap;
	    imageInfo.flowLocation = newFlowLocation;
	    imageInfo.flowElements = flowElements;
	    imageInfo.opinionMap = opinionMap;
	    imageInfo.stacks = stacks;
	    this.scope.subImageInfo = imageInfo;

	    var flowImageCanvas = new flowCanvas ( this.scope , nodeId + "_flowImageCanvas" , imageInfo , true );
	    var drawFn = flowImageCanvas.initDraw ( );
	    $ ( "#flowImgModalTitle" ).text ( node.taskName + '-动态任务流程图' );
	    $ ( "#flowImgModal" ).modal ( 'show' );
	    $ ( "#flowImgModalBody" ).html ( flowImageCanvas.canvas );
	    drawFn.drawFlowImage ( ); // 重构后的代码
    } ,
    // 绘制多实例
    drawMutiInstFlow : function ( node , opinion , type ) {
	    if ( type && type != 'opinion' ) {
		    return;
	    }

	    if ( this.createCanvas ) {
		    // 不再打开二级多实例
		    return false;
	    }

	    var opinionNodeId = "";
	    var scope = this.scope;
	    if ( !node || !node.multInst ) {

		    // 如果是从意见节点处打开的,则遍历获取之前的节点 看是否有分发的
		    if ( opinion ) {
			    function getPreNodeMutiInst ( tempNode ) {

				    if ( !tempNode ) {
					    return null;
				    }
				    if ( tempNode.multInst == 'start' ) {
					    return tempNode;
				    }
				    return getPreNodeMutiInst ( tempNode.preNodeMap );
			    }
			    node = getPreNodeMutiInst ( opinion.node );
		    }

		    if ( !node || !node.multInst ) {
			    return;
		    }
		    opinionNodeId = opinion.node.id;
	    } else if ( opinion ) {
		    // 如果是分发节点的意见点击事件,则不触发
		    return;
	    }

	    var nodeId = node.id;

	    // 获取nodeId 与 回收节点之间的节点信息
	    var imageInfo = {};
	    var startNodeId = nodeId;
	    if ( node.multInst == 'start' ) {
		    imageInfo = this.getFLowImageInfo ( nodeId , node.multInst_end );
	    } else {
		    startNodeId = node.multInst_start;
		    imageInfo = this.getFLowImageInfo ( node.multInst_start , nodeId );
	    }

	    if ( !imageInfo ) {
		    return;
	    }

	    if ( !opinionNodeId ) {
		    opinionNodeId = imageInfo.nodeMap[imageInfo.startNodeId].nextNodeMap.id;
	    }

	    var nodeOpinions = imageInfo.opinionMap[opinionNodeId];
	    if ( !nodeOpinions ) {
		    // 如果还没有到这个节点,就直接绘制
		    this.drawDynamicFlow ( node );
		    return;
	    }

	    this.scope.selectedOpinions = nodeOpinions;

	    this.openOpinionFlow ( opinion || nodeOpinions[nodeOpinions.length - 1] , imageInfo );

    } ,
    openOpinionFlow : function ( nodeOpinion , imageInfo ) {

	    var scope = this.scope;
	    scope.selectedOpinion = nodeOpinion;
	    var instanceId = "";
	    var defId = "";
	    var taskId = "";
	    var name = "";
	    instanceId = nodeOpinion.instId;
	    // defId = nodeOpinion.mainDefId;
	    taskId = nodeOpinion.taskId;
	    name = nodeOpinion.taskName + nodeOpinion.trace;

	    // 获取流程节点坐标
	    var defer1 = baseService.postForm ( api + "/scabpm/bpm/instance/getFlowImageInfo" , {
	        instanceId : instanceId ,
	        defId : defId ,
	        taskId : taskId
	    } );

	    $.getResultData ( defer1 , function ( data ) {

		    data.nodeMap = imageInfo.nodeMap;
		    data.flowLocation = imageInfo.flowLocation;
		    scope.subImageInfo = data;
		    var flowImageCanvas = new flowCanvas ( scope , imageInfo.startNodeId + "_flowImageCanvas" , data , true );
		    var drawFn = flowImageCanvas.initDraw ( );
		    $ ( "#flowImgModalTitle" ).text ( name );
		    $ ( "#flowImgModal" ).modal ( 'show' );
		    $ ( "#flowImgModalBody" ).html ( flowImageCanvas.canvas );
		    drawFn.drawFlowImage ( ); // 重构后的代码
	    } )
    } ,
    // 打开外部子流程
    openCallActivity : function ( node , subInst , type ) {

	    if ( !node || node.type != "CallActivity" ) {
		    return;
	    }

	    if ( type && type != "subInst" ) {
		    return;
	    }

	    if(type!="subInst"){
			window.hisSubFlow = window.hisSubFlow || [];
			window.hisSubFlow.push({
				scope:this,
				node:node,
				subInst:subInst,
				type:type
			});
		}

	    var scope = this.scope;
	    var nodeId = node.id;
	    var instanceId = "";
	    var defId = "";
	    var taskId = "";
	    var name = "";

	    var subDefinition = node.subDefinition;
	    if ( subDefinition ) {
		    instanceId = subDefinition.instanceId;
		    defId = subDefinition.id;
		    taskId = subDefinition.taskId;
		    name = subDefinition.name;
	    }
	    var subInstanceList = node.subInstanceList;
	    if ( subInstanceList && subInstanceList.length > 0 ) {

		    var selectedSubInst = subInst;
		    if ( !selectedSubInst ) {
			    selectedSubInst = subInstanceList[subInstanceList.length - 1];
		    }

		    this.scope.selectedSubInstanceList = subInstanceList;
		    this.scope.selectedSubInst = selectedSubInst;
		    instanceId = selectedSubInst.id;
		    defId = selectedSubInst.defId;
		    taskId = selectedSubInst.taskId;
		    name = selectedSubInst.defName;
	    }

	    // 获取流程节点坐标
	    var defer1 = baseService.postForm ( __ctx + "/bpm/instance/getFlowImageInfo" , {
	        instanceId : instanceId ,
	        defId : defId ,
	        taskId : taskId
	    } );

	    $.getResultData ( defer1 , function ( data ) {

		    scope.subImageInfo = data;
		    var flowImageCanvas = new flowCanvas ( scope , nodeId + "_flowImageCanvas" , data , true );
		    var drawFn = flowImageCanvas.initDraw ( );
		    $ ( "#flowImgModalTitle" ).text ( name );
		    $ ( "#flowImgModal" ).modal ( 'show' );
		    $ ( "#flowImgModalBody" ).html ( flowImageCanvas.canvas );
		    drawFn.drawFlowImage ( ); // 重构后的代码
	    } )

    } ,
    // 绘制多实例动态流程图
    drawDynamicFlow : function ( node ) {

	    if ( !node || !node.multInst ) {
		    return;
	    }

	    var scope = this.scope;
	    var nodeId = node.id;
	    // 获取nodeId 与 回收节点之间的节点信息
	    var imageInfo = {};
	    if ( node.multInst == 'start' ) {
		    imageInfo = this.getFLowImageInfo ( nodeId , node.multInst_end );
	    } else {
		    imageInfo = this.getFLowImageInfo ( node.multInst_start , nodeId );
	    }

	    this.scope.subImageInfo = imageInfo;
	    var flowImageCanvas = new flowCanvas ( this.scope , nodeId + "_flowImageCanvas" , imageInfo , true );
	    var drawFn = flowImageCanvas.initDraw ( );
	    $ ( "#flowImgModalTitle" ).text ( drawFn.imageInfo.flowElementJson[nodeId].name + '-多实例分发流程图' );
	    $ ( "#flowImgModal" ).modal ( 'show' );
	    $ ( "#flowImgModalBody" ).html ( flowImageCanvas.canvas );
	    drawFn.drawFlowImage ( ); // 重构后的代码
    } ,
    drawNode : function ( ) {

	    // 先绘制子流程,泳道池
	    this.drawNodeFn ( [ "subProcess" , "callActivity" , "pool" , "lane" ] );

	    // 再绘制其他节点
	    this.drawNodeFn ( [ "startEvent" , "endEvent" , "textAnnotation" , "serviceTask" , "exclusiveGateway" ,//
	    "parallelGateway" , "inclusiveGateway" , "startTimer" , "boundaryTimer" , "catchTimer" , "startSignal" ,//
	    "boundarySignal" , "catchSignal" , "throwSignal" , "endError" , "boundaryError" , "endTerminate" , "userTask" ,//
	    "mutiUserTask" , "mutiInstTaskStart" , "mutiInstTaskEnd" , "dynamicTask" ,//
	    ] );
    } ,
    
    // 用来测试用的, 
    initTestNode : function(){

    	this.imageInfo.nodeMap = [];
    	var stackJson = this.stackJson;
    	var n1 = {
    		id:"UserTask1",
    		x : 500,y:500,width:100,height:80,
    		nodeType: "userTask",
    		nodeName: "驳回中心",
    		color: "rgb(26, 179, 148)",
    		type: "UserTask",
    		nextNodeMap : n2,
    		preNodeMap : n1
    	}
    	
    	var xof = 300;
    	var yof = 200;

    	var n2 = {
    		id:"UserTask2",
    		x : n1.x - xof,y:n1.y - yof,width:100,height:80,
    		nodeType: "userTask",
    		nodeName: "驳回到2",
    		color: "rgb(26, 179, 148)",
    		type: "UserTask",
//        		nextNodeMap : n2,
    		preNodeMap : n1
    	}

    	var n3 = {
    		id:"UserTask3",
    		x : n1.x - xof,y:n1.y - yof,width:100,height:80,
    		nodeType: "userTask",
    		nodeName: "驳回到3",
    		color: "rgb(26, 179, 148)",
    		type: "UserTask",
//        		nextNodeMap : n2,
    		preNodeMap : n1
    	}

    	var n4 = {
    		id:"UserTask4",
    		x : n1.x + xof,y:n1.y - yof,width:100,height:80,
    		nodeType: "userTask",
    		nodeName: "驳回到4",
    		color: "rgb(26, 179, 148)",
    		type: "UserTask",
//        		nextNodeMap : n2,
    		preNodeMap : n1
    	}

    	var n5 = {
    		id:"UserTask5",
    		x : n1.x + xof,y:n1.y + yof,width:100,height:80,
    		nodeType: "userTask",
    		nodeName: "驳回到5",
    		color: "rgb(26, 179, 148)",
    		type: "UserTask",
//        		nextNodeMap : n2,
    		preNodeMap : n1
    	}

    	var n6 = {
    		id:"UserTask6",
    		x : n1.x + xof,y:n1.y,width:100,height:80,
    		nodeType: "userTask",
    		nodeName: "驳回到6",
    		color: "rgb(26, 179, 148)",
    		type: "UserTask",
//        		nextNodeMap : n2,
    		preNodeMap : n1
    	}

    	var n7 = {
    		id:"UserTask7",
    		x : n1.x - xof,y:n1.y,width:100,height:80,
    		nodeType: "userTask",
    		nodeName: "驳回到7",
    		color: "rgb(26, 179, 148)",
    		type: "UserTask",
//        		nextNodeMap : n2,
    		preNodeMap : n1
    	}

    	var n8 = {
    		id:"UserTask8",
    		x : n1.x,y:n1.y + yof,width:100,height:80,
    		nodeType: "userTask",
    		nodeName: "驳回到8",
    		color: "rgb(26, 179, 148)",
    		type: "UserTask",
//        		nextNodeMap : n2,
    		preNodeMap : n1
    	}

    	var n9 = {
    		id:"UserTask9",
    		x : n1.x,y:n1.y - yof,width:100,height:80,
    		nodeType: "userTask",
    		nodeName: "驳回到9",
    		color: "rgb(26, 179, 148)",
    		type: "UserTask",
//        		nextNodeMap : n2,
    		preNodeMap : n1
    	}

    	var n10 = {
    		id:"UserTask10",
    		x : n1.x-xof,y:n1.y + yof,width:100,height:80,
    		nodeType: "userTask",
    		nodeName: "驳回到10",
    		color: "rgb(26, 179, 148)",
    		type: "UserTask",
//        		nextNodeMap : n2,
    		preNodeMap : n1
    	}
    	
    	stackJson.back = {
    		nodeName: n1.id+"-》"+n2.id,
    		color: "rgb(0, 255, 0)"
    	}
    	
    	stackJson.back1 = {
    		nodeName: n1.id+"-》"+n3.id,
    		color: "rgb(0, 255, 255)"
    	}
    	stackJson.back2 = {
        		nodeName: n1.id+"-》"+n4.id,
        		color: "rgb(255, 255, 255)"
        	}
    	stackJson.back3 = {
        		nodeName: n1.id+"-》"+n5.id,
        		color: "rgb(26, 179, 148)"
        	}
    	stackJson.back4 = {
        		nodeName: n1.id+"-》"+n6.id,
        		color: "rgb(26, 179, 148)"
        	}
    	stackJson.back5 = {
        		nodeName: n1.id+"-》"+n7.id,
        		color: "rgb(26, 179, 148)"
        	}
    	stackJson.back6 = {
        		nodeName: n1.id+"-》"+n8.id,
        		color: "rgb(26, 179, 148)"
        	}
    	stackJson.back7 = {
        		nodeName: n1.id+"-》"+n9.id,
        		color: "rgb(26, 179, 148)"
        	}
    	stackJson.back8 = {
    			nodeName: n1.id+"-》"+n10.id,
        		color: "rgb(26, 179, 148)"
    	}
    	
    	this.imageInfo.nodeMap[n1.id] = n1;
    	this.imageInfo.nodeMap[n2.id] = n2;
    	this.imageInfo.nodeMap[n3.id] = n3;
    	this.imageInfo.nodeMap[n4.id] = n4;
    	this.imageInfo.nodeMap[n5.id] = n5;
    	this.imageInfo.nodeMap[n6.id] = n6;
    	this.imageInfo.nodeMap[n7.id] = n7;
    	this.imageInfo.nodeMap[n8.id] = n8;
    	this.imageInfo.nodeMap[n9.id] = n9;
    	this.imageInfo.nodeMap[n10.id] = n10;
    	
    },
    // 绘制驳回的效果 - 这种是以两个矩形计算出四个点来连线
    drawBohui : function(){

	    var nodeMap = this.imageInfo.nodeMap;
	    var nodeTypeJson = this.nodeTypeJson;
	    var flowElementJson = this.imageInfo.flowElementJson;
	    var opinionMap = this.imageInfo.opinionMap || {};
	    var nodeNameJson = this.nodeNameJson;
	    var stacks = this.imageInfo.stacks || [];
	    var flowColorJson = this.flowColorJson;
	    var flowLocation = this.flowLocation;
	    var xoffset = this.xoffset;
	    var yoffset = this.yoffset;

	    for ( var i = 0 ; i< stacks.length ; i++ ) {
	    	if(stacks[i].actionName == "reject" && stacks[i].nodeId == "back"){
	    		//  "UserTask4-》UserTask3"
	    		var stack = stacks[i];
	    		var startNode = nodeMap[stack.nodeName.split("-》")[0]];
	    		var endNode = nodeMap[stack.nodeName.split("-》")[1]];
	    		var startPoint = {x:startNode.x,y:startNode.y,w:startNode.width,h:startNode.height};
	    		var endPoint = {x:endNode.x,y:endNode.y,w:endNode.width,h:endNode.height};
	    		
	    		var points = this.getPointFromTwoBox(startNode,endNode);
	    		for(var pi = 0 ; pi < points.length - 1 ; pi++){
				    var flowFrom = points[pi];
				    var flowTo = points[pi + 1];

				    var arrow = new Arrow ( flowFrom.x - xoffset , flowFrom.y - yoffset , flowTo.x - xoffset , flowTo.y - yoffset );
				    arrow.setColor ( Color.PINK );
	  				arrow.setDash ( true );// 虚线
				    // 线段的最后一条折线才需要箭头
				    if ( pi == ( points.length - 2 ) ) {
					    arrow.setHead ( true );
				    }
				    arrow.draw ( this.cxt );
	    		}
	    	}
	    }
	    
    },
    //  绘制驳回的效果 - 这种是用定点的方式连线
    drawBohui_back : function(){
    	
    	var nodeMap = this.imageInfo.nodeMap;
    	var nodeTypeJson = this.nodeTypeJson;
    	var flowElementJson = this.imageInfo.flowElementJson;
    	var opinionMap = this.imageInfo.opinionMap || {};
    	var nodeNameJson = this.nodeNameJson;
    	var stackJson = this.stackJson;
    	var flowColorJson = this.flowColorJson;
    	var flowLocation = this.flowLocation;
    	var xoffset = this.xoffset;
    	var yoffset = this.yoffset;
    	for ( var i in stackJson ) {
    		if(i=="back"){
    			//  "UserTask4-》UserTask3"
    			var stack = stackJson[i];
    			var startNode = nodeMap[stack.nodeName.split("-》")[0]];
    			var endNode = nodeMap[stack.nodeName.split("-》")[1]];
    			var startPoint = {x:startNode.x,y:startNode.y,w:startNode.width,h:startNode.height};
    			var endPoint = {x:endNode.x,y:endNode.y,w:endNode.width,h:endNode.height};
    			
    			var minDist = this.getMinDistancePoint([
    				{x:startPoint.x,y:startPoint.y},
    				{x:startPoint.x+startPoint.w,y:startPoint.y},
    				{x:startPoint.x,y:startPoint.y+startPoint.h},
    				{x:startPoint.x+startPoint.w,y:startPoint.y+startPoint.h},
    				],[
    					{x:endPoint.x,y:endPoint.y},
    					{x:endPoint.x+endPoint.w,y:endPoint.y},
    					{x:endPoint.x,y:endPoint.y+endPoint.h},
    					{x:endPoint.x+endPoint.w,y:endPoint.y+endPoint.h},
    					]);
    			var flowFrom = minDist.s;
    			var flowTo = minDist.e;
    			var deg = this.getAngle ( flowFrom.x , flowFrom.y , flowTo.x , flowTo.y );
    			
    			var arrow = new Arrow ( flowFrom.x - xoffset , flowFrom.y - yoffset , flowTo.x - xoffset , flowTo.y - yoffset );
    			arrow.setColor ( Color.PINK );
    			arrow.setHead ( true );
    			arrow.setDash ( true );// 虚线
    			arrow.draw ( this.cxt );
    			break;
    		}
    	}
    	
    },
    // 绘制多实例的效果 - 后来决定只写到原来的线
    drawDuoshili : function(startNode,endNode,count){

    	var flow = this.imageInfo.flowLocation[startNode.flowElement.outgoingFlows[0].id]
    	var xoffset = this.xoffset;
    	var yoffset = this.yoffset;
	    var color = Color.success;
	    var flowFrom = flow[0];
	    var flowTo = flow[1];
	    this.drawTxt ( flowFrom.x - xoffset , flowFrom.y - yoffset , flowTo.x - xoffset , flowTo.y - yoffset , count , color );
	    
    },
    // 绘制多实例的效果 - 矩形的方式
    drawDuoshili_back2 : function(startNode,endNode,count){

	    var nodeMap = this.imageInfo.nodeMap;
	    var nodeTypeJson = this.nodeTypeJson;
	    var flowElementJson = this.imageInfo.flowElementJson;
	    var opinionMap = this.imageInfo.opinionMap || {};
	    var nodeNameJson = this.nodeNameJson;
	    var stackJson = this.stackJson;
	    var flowColorJson = this.flowColorJson;
	    var flowLocation = this.flowLocation;
	    var xoffset = this.xoffset;
	    var yoffset = this.yoffset;
	    
		var points = this.getPointFromTwoBox(startNode,endNode);
		for(var pi = 0 ; pi < points.length - 1 ; pi++){
		    var flowFrom = points[pi];
		    var flowTo = points[pi + 1];

		    var arrow = new Arrow ( flowFrom.x - xoffset , flowFrom.y - yoffset , flowTo.x - xoffset , flowTo.y - yoffset );
		    arrow.setColor ( stackJson.color || Color.PINK );
				arrow.setDash ( true );// 虚线
		    // 线段的最后一条折线才需要箭头
		    if ( pi == ( points.length - 2 ) ) {
			    arrow.setHead ( true );
		    }
		    arrow.draw ( this.cxt );
		}

	    var color = Color.todo;
	    var flowFrom = points[1];
	    var flowTo = points[2];
	    this.drawTxt ( flowFrom.x - xoffset , flowFrom.y - yoffset , flowTo.x - xoffset , flowTo.y - yoffset , count , color );
	    
    },
    // 绘制多实例的效果 -- 两个点的方式
    drawDuoshili_back : function(startNode,endNode,count){
    	
    	var nodeMap = this.imageInfo.nodeMap;
    	var nodeTypeJson = this.nodeTypeJson;
    	var flowElementJson = this.imageInfo.flowElementJson;
    	var opinionMap = this.imageInfo.opinionMap || {};
    	var nodeNameJson = this.nodeNameJson;
    	var stackJson = this.stackJson;
    	var flowColorJson = this.flowColorJson;
    	var flowLocation = this.flowLocation;
    	var xoffset = this.xoffset;
    	var yoffset = this.yoffset;
    	
    	var startPoint = {x:startNode.x,y:startNode.y,w:startNode.width,h:startNode.height};
    	var endPoint = {x:endNode.x,y:endNode.y,w:endNode.width,h:endNode.height};
    	
    	var minDist = this.getMinDistancePoint([
    		{x:startPoint.x,y:startPoint.y},
    		{x:startPoint.x+startPoint.w,y:startPoint.y},
    		{x:startPoint.x,y:startPoint.y+startPoint.h},
    		{x:startPoint.x+startPoint.w,y:startPoint.y+startPoint.h},
    		],[
    			{x:endPoint.x,y:endPoint.y},
    			{x:endPoint.x+endPoint.w,y:endPoint.y},
    			{x:endPoint.x,y:endPoint.y+endPoint.h},
    			{x:endPoint.x+endPoint.w,y:endPoint.y+endPoint.h},
    			]);
    	var flowFrom = minDist.s;
    	var flowTo = minDist.e;
    	var deg = this.getAngle ( flowFrom.x , flowFrom.y , flowTo.x , flowTo.y );
    	
    	var color = Color.todo;
    	var arrow = new Arrow ( flowFrom.x - xoffset , flowFrom.y - yoffset , flowTo.x - xoffset , flowTo.y - yoffset );
    	arrow.setColor ( color );
    	arrow.setHead ( true );
    	arrow.setDash ( true );// 虚线
    	arrow.draw ( this.cxt );
    	
    	this.drawTxt ( flowFrom.x - xoffset , flowFrom.y - yoffset , flowTo.x - xoffset , flowTo.y - yoffset , count , color );
    	
    },
    // 绘制节点
    drawNodeFn : function ( nodeTypes ) {

	    var nodeMap = this.imageInfo.nodeMap;
	    var nodeTypeJson = this.nodeTypeJson;
	    var flowElementJson = this.imageInfo.flowElementJson;
	    var opinionMap = this.imageInfo.opinionMap || {};
	    var nodeNameJson = this.nodeNameJson;
	    var stackJson = this.stackJson;
	    var flowColorJson = this.flowColorJson;
	    var flowLocation = this.flowLocation;
	    var xoffset = this.xoffset;
	    var yoffset = this.yoffset;

	    // 绘制节点
	    for ( var i in nodeMap ) {
		    var xyz = nodeMap[i];
		    var nodeType = xyz.nodeType;
		    var nodeName = xyz.nodeName;
//		    nodeName = nodeName+nodeName+nodeName
		    var flowElement = xyz.flowElement;
		    var color = xyz.color;
		    if ( nodeTypes.indexOf ( nodeType ) < 0 ) {
			    continue;
		    }

		    // 绘制线条上的文字,顺序不能放在后面,否则会被覆盖
		    this.drawLineTxt ( xoffset , yoffset , flowElement );

		    switch ( nodeType ) {
			    case "startEvent" :
			    case "endEvent" :
				    this.drawCircle ( xyz.x - xoffset , xyz.y - yoffset , xyz.width , xyz.height , color , '' , xyz.icon , xyz.reg );//
					this.drawCircleTxt ( xyz.x - xoffset , xyz.y - yoffset , xyz.width , xyz.height , color , nodeName , xyz.icon );//
			    break;
			    case "subProcess" :// 子流程
				    this.drawSubProcess ( xyz.x - xoffset , xyz.y - yoffset , xyz.width , xyz.height , nodeName , "" , color );//
			    break;
			    case "callActivity" : // 外部子流程
				    this.drawCallActivity ( xyz.x - xoffset , xyz.y - yoffset , xyz.width , xyz.height , nodeName , "" , color );//
			    break;
			    case "textAnnotation" :
				    this.drawTextAnnotation ( xyz.x - xoffset , xyz.y - yoffset , xyz.width , xyz.height , nodeName , color );//
			    break;
			    case "serviceTask" : // 服务
				    this.drawTaskNode ( xyz.x - xoffset , xyz.y - yoffset , xyz.width , xyz.height , nodeName , "serviceIcon" , color );//
			    break;
			    case "exclusiveGateway" : // 分支网关
				    this.drawExclusiveGateway ( xyz.x - xoffset , xyz.y - yoffset , nodeName , color );
			    break;
			    case "parallelGateway" : // 并行网关
				    this.drawParallelGateway ( xyz.x - xoffset , xyz.y - yoffset , nodeName , color );

				    // nodeName = "";
				    // drawBoundaryTimer ( xyz.x-xoffset , xyz.y-yoffset, 30,
				    // 30, color, nodeName );//
				    // drawStartSignalEvent ( xyz.x-xoffset + 40 ,
				    // xyz.y-yoffset, 30, 30, color, nodeName );//
				    // drawBoundarySignalEvent ( xyz.x-xoffset + 80 ,
				    // xyz.y-yoffset, 30, 30, color, nodeName );//
				    // drawEndErrorEvent ( xyz.x-xoffset + 120 , xyz.y-yoffset,
				    // 30, 30, color, nodeName );//
				    // drawBoundaryErrorEvent ( xyz.x-xoffset + 160 ,
				    // xyz.y-yoffset, 30, 30, color, nodeName );//
				    // drawEndTerminateEvent ( xyz.x-xoffset + 200 ,
				    // xyz.y-yoffset, 30, 30, color, nodeName );//
				    //    				

			    break;
			    case "inclusiveGateway" : // 并行条件网关
				    this.drawInclusiveGateway ( xyz.x - xoffset , xyz.y - yoffset , nodeName , color );

			    break;
			    case "startTimer" : // 定时开始节点
			    case "boundaryTimer" : // 定时事件边界
			    case "catchTimer" : // 定时等待节点
				    this.drawBoundaryTimer ( xyz.x - xoffset , xyz.y - yoffset , xyz.width , xyz.height , color , nodeName );//
			    break;
			    case "startSignal" : // 信号开始事件
			    case "boundarySignal" : // 信号边界事件
			    case "catchSignal" : // 信号等待节点
				    this.drawStartSignalEvent ( xyz.x - xoffset , xyz.y - yoffset , xyz.width , xyz.height , color , nodeName );//
			    break;
			    case "throwSignal" : // 信号抛出节点
				    this.drawBoundarySignalEvent ( xyz.x - xoffset , xyz.y - yoffset , xyz.width , xyz.height , color , nodeName );//
			    break;
			    case "endError" : // 结束错误事件
				    this.drawEndErrorEvent ( xyz.x - xoffset , xyz.y - yoffset , xyz.width , xyz.height , color , nodeName );//
			    break;
			    case "boundaryError" : // 错误边界事件
				    this.drawBoundaryErrorEvent ( xyz.x - xoffset , xyz.y - yoffset , xyz.width , xyz.height , color , nodeName );//
			    break;
			    case "endTerminate" : // 结束终止事件
				    this.drawEndTerminateEvent ( xyz.x - xoffset , xyz.y - yoffset , xyz.width , xyz.height , color , nodeName );//
			    break;
			    case "pool" : // 池
			    case "lane" : // 
				    xyz.hideDiv = true;
				    this.drawPool ( xyz.x - xoffset , xyz.y - yoffset , xyz.width , xyz.height , nodeName , "" , color );//
			    break;
			    case "mutiUserTask" : // 会签任务
				    this.drawTaskNode ( xyz.x - xoffset , xyz.y - yoffset , xyz.width , xyz.height , nodeName , "mutiUserIcon" , color );//
			    break;
			    case "mutiInstTaskStart" : // 多实例任务
			    	// 判断下个节点分发了多少个实例
			    	var list = [];
			    	var json = {};
			    	if(xyz.nextNodeMap && xyz.nextNodeMap.opinions){
			    		for(var i = 0 ; i < xyz.nextNodeMap.opinions.length ; i++){
				    		var trace = xyz.nextNodeMap.opinions[i].trace;
				    		if(!json[trace]){
				    			list.push(trace);
				    			json[trace] = true;
				    		}
				    	}
			    	}
			    	
			    	// 绘制多实例之间的线条
			    	if(list.length>1){
			    		this.drawDuoshili(xyz,xyz.nextNodeMap,list.length);
			    	}
				    this.drawTaskNode ( xyz.x - xoffset , xyz.y - yoffset , xyz.width , xyz.height , nodeName , "mutiInstIcon" , color , -90 );//
			    break;
			    case "mutiInstTaskEnd" : // 多实例任务
				    this.drawTaskNode ( xyz.x - xoffset , xyz.y - yoffset , xyz.width , xyz.height , nodeName , "mutiInstIcon" , color , 90 );//
			    break;
			    case "dynamicTask" : // 动态任务
				    // 获取动态任务节点的执行情况
				    if ( stackJson[i] && stackJson[i].actionName == 'create' ) {
					    xyz.dynamicTaskCounts = stackJson[i].nodeName.split ( "-" )[stackJson[i].nodeName.split ( "-" ).length - 1] + "/" + xyz.dynamicTaskCounts;
				    }
				    this.drawDynamicNode ( xyz.x - xoffset , xyz.y - yoffset , xyz.width , xyz.height , nodeName , "dynamicTaskIcon" , color , xyz.dynamicTaskCounts );//
			    break;
			    case "userTask" : // 用户任务
			    default :
				    this.drawTaskNode ( xyz.x - xoffset , xyz.y - yoffset , xyz.width , xyz.height , nodeName , "userIcon" , color );//
			    break;
		    }
	    }
    } ,
    // 绘制线条
    drawLine : function ( ) {

	    var flowLocation = this.flowLocation;
	    var xoffset = this.xoffset;
	    var yoffset = this.yoffset;
	    // 绘制线条
	    for ( var i in flowLocation ) {
		    var stack = this.stackJson[i];
		    var flows = flowLocation[i];
		    var color = this.flowColorJson[i] || this.Color.DEFAULT;
		    for ( var j = 0 ; j < flows.length - 1 ; j++ ) {
			    var flowFrom = flows[j];
			    var flowTo = flows[j + 1];

			    var arrow = new Arrow ( flowFrom.x - xoffset , flowFrom.y - yoffset , flowTo.x - xoffset , flowTo.y - yoffset );
			    arrow.setColor ( color );
			    // 线段的最后一条折线才需要箭头
			    if ( j == ( flows.length - 2 ) ) {
				    arrow.setHead ( true );
			    }
			    arrow.draw ( this.cxt );
		    }
	    }
    } ,
    // 绘制圆形
    drawCircle : function ( x , y , w , h , color , lineWidth , icon , revertIconReg ) {

	    var r = h / 2;

	    var cxt = this.cxt;
	    var r = h / 2;
	    cxt.beginPath ( );
	    cxt.moveTo ( x + r , y );
	    cxt.arcTo ( x + w , y , x + w , y + h , r );
	    cxt.arcTo ( x + w , y + h , x , y + h , r );
	    cxt.arcTo ( x , y + h , x , y , r );
	    cxt.arcTo ( x , y , x + w , y , r );
	    cxt.closePath ( );
	    cxt.strokeStyle = color || 'black';
	    cxt.lineWidth = lineWidth || 2;
	    cxt.stroke ( );

	    if ( icon ) {
		    // cxt.font = "normal 14px 微软雅黑";
		    cxt.fillStyle = color;
		    var iconEl = document.getElementById ( icon );
		    if ( iconEl ) {
			    icon = document.getElementById ( icon ).textContent;
			    cxt.font = "bold 18px FontAwesome";
		    } else {
			    cxt.font = "bold 12px FontAwesome";
		    }

		    // 设置图标旋转角度
		    if ( revertIconReg ) {
			    var centerX = x + 25;
			    var centerY = y + 10;
			    if ( revertIconReg == -90 ) {
				    centerX = x + 20;
				    centerY = y + 25;
			    } else if ( revertIconReg == 90 ) {
				    centerX = x + 10;
				    centerY = y + 10;
			    }
			    var ang = revertIconReg * Math.PI / 180;
			    cxt.translate ( centerX , centerY );
			    cxt.rotate ( ang );
			    cxt.fillText ( icon , 0 , 0 ); // 绘制文字 这里的0和0 搞了我好久

			    cxt.rotate ( -ang );
			    cxt.translate ( -centerX , -centerY );
		    } else {
			    cxt.fillText ( icon , x + 5 , y + 19 , 100 ); // 绘制文字
		    }
	    }

    } ,
    // 绘制圆形的文字
    drawCircleTxt : function ( x , y , w , h , color , txt , icon ) {

	    if ( txt ) {
		    var cxt = this.cxt;
		    cxt.font = "bold 12px 微软雅黑";
		    // 测量宽度
		    var txtWidth = cxt.measureText ( txt ).width;
		    txtWidth = txtWidth / 2 - 18;
			cxt.fillStyle = color;
		    cxt.fillText ( txt , x - txtWidth , y - 4 , 100 );
	    }
    } ,
    // 绘制带有图标的圆形
    drawIconCircle : function ( x , y , w , h , color , txt , icon ) {

	    var cxt = this.cxt;
	    this.drawCircle ( x , y , w , h , color , 4 );
	    cxt.fillStyle = "#fff";
	    cxt.fill ( );
	    cxt.font = "normal 26px FontAwesome";
	    cxt.fillStyle = color;
	    var icon = document.getElementById ( icon ).textContent
	    cxt.fillText ( icon , x + 4 , y + 24 , 100 ); // 绘制图标

	    this.drawCircleTxt ( x , y , w , h , color , txt , icon );
    } ,
    // 绘制定时边界事件
    drawBoundaryTimer : function ( x , y , w , h , color , txt ) {

	    var cxt = this.cxt;
	    this.drawCircle ( x , y , w , h , color , 2 );
	    cxt.fillStyle = "#fff";
	    cxt.fill ( );
	    cxt.font = "normal 26px FontAwesome";
	    cxt.fillStyle = color;
	    var icon = document.getElementById ( "timerIcon" ).textContent
	    cxt.fillText ( icon , x + 4 , y + 25 , 100 ); // 绘制图标
	    this.drawCircleTxt ( x , y , w , h , color , txt , icon );
    } ,
    // 绘制 定时开始节点
    drawStartSignalEvent : function ( x , y , w , h , color , txt ) {

	    var cxt = this.cxt;
	    this.drawCircle ( x , y , w , h , color , 4 );
	    cxt.fillStyle = "#fff";
	    cxt.fill ( );
	    cxt.font = "normal 40px FontAwesome";
	    cxt.fillStyle = color;
	    var icon = document.getElementById ( "upIcon" ).textContent
	    cxt.fillText ( icon , x + 4 , y + 25 , 100 ); // 绘制图标
	    this.drawCircleTxt ( x , y , w , h , color , txt , icon );
    } ,

    // 绘制 结束错误事件
    drawEndErrorEvent : function ( x , y , w , h , color , txt ) {

	    var cxt = this.cxt;
	    this.drawCircle ( x , y , w , h , color , 4 );
	    cxt.fillStyle = "#fff";
	    cxt.fill ( );
	    cxt.font = "normal 23px FontAwesome";
	    cxt.fillStyle = color;
	    var icon = document.getElementById ( "endErrorIcon" ).textContent
	    cxt.fillText ( icon , x + 6 , y + 23 , 100 ); // 绘制图标
	    this.drawCircleTxt ( x , y , w , h , color , txt , icon );
    } ,
    // 绘制 错误边界事件
    drawBoundaryErrorEvent : function ( x , y , w , h , color , txt ) {

	    var cxt = this.cxt;
	    this.drawCircle ( x , y , w , h , color , 4 );
	    cxt.fillStyle = "#fff";
	    cxt.fill ( );
	    cxt.font = "normal 23px FontAwesome";
	    cxt.fillStyle = color;
	    var icon = document.getElementById ( "boundaryErrorIcon" ).textContent
	    cxt.fillText ( icon , x + 9 , y + 24 , 100 ); // 绘制图标
	    this.drawCircleTxt ( x , y , w , h , color , txt , icon );
    } ,
    // 绘制 错误边界事件
    drawEndTerminateEvent : function ( x , y , w , h , color , txt ) {

	    var cxt = this.cxt;
	    this.drawCircle ( x , y , w , h , color , 6 );
	    cxt.fillStyle = "#fff";
	    cxt.fill ( );
	    cxt.font = "normal 26px FontAwesome";
	    cxt.fillStyle = color;
	    var icon = document.getElementById ( "endIcon" ).textContent
	    cxt.fillText ( icon , x + 4 , y + 24 , 100 ); // 绘制图标
	    this.drawCircleTxt ( x , y , w , h , color , txt , icon );
    } ,
    // 绘制 信号边界事件
    drawBoundarySignalEvent : function ( x , y , w , h , color , txt ) {

	    var cxt = this.cxt;
	    this.drawCircle ( x , y , w , h , color , 1 );
	    var sub = 8;
	    var subHalf = sub / 2;
	    this.drawCircle ( x + subHalf , y + subHalf , w - sub , h - sub , color , 1 );
	    cxt.fillStyle = "#fff";
	    cxt.fill ( );
	    cxt.font = "normal 32px FontAwesome";
	    cxt.fillStyle = color;
	    var icon = document.getElementById ( "upIcon" ).textContent
	    cxt.fillText ( icon , x + 6 , y + 23 , 100 ); // 绘制图标
	    this.drawCircleTxt ( x , y , w , h , color , txt , icon );
    } ,
    // 绘制 圆角矩形
    drawRoundedRect : function ( rect , color , r ) {

	    // $ ( this.canvas ).drawRect ( {
	    // // layer: true,
	    // // draggable: true,
	    // strokeStyle : color ,
	    // strokeWidth : 1 ,
	    // cornerRadius : r ,
	    // x : rect.x + rect.w / 2 ,
	    // y : rect.y + rect.h / 2 ,
	    // width : rect.w ,
	    // height : rect.h ,
	    // opacity: 0.5,
	    // intangible: true,
	    // cursors: {
	    // // 显示悬停时的指点器
	    // mouseover: 'pointer',
	    // // 显示鼠标按下时的'move'指针
	    // mousedown: 'move',
	    // // 在鼠标弹起时反转指针
	    // mouseup: 'pointer'
	    // }
	    // } );

	    var cxt = this.cxt;
	    cxt.lineWidth = 1;
	    var ptA = this.Point ( rect.x + r , rect.y );
	    var ptB = this.Point ( rect.x + rect.width , rect.y );
	    var ptC = this.Point ( rect.x + rect.width , rect.y + rect.height );
	    var ptD = this.Point ( rect.x , rect.y + rect.height );
	    var ptE = this.Point ( rect.x , rect.y );

	    cxt.beginPath ( );

	    cxt.moveTo ( ptA.x , ptA.y );
	    cxt.arcTo ( ptB.x , ptB.y , ptC.x , ptC.y , r );
	    cxt.arcTo ( ptC.x , ptC.y , ptD.x , ptD.y , r );
	    cxt.arcTo ( ptD.x , ptD.y , ptE.x , ptE.y , r );
	    cxt.arcTo ( ptE.x , ptE.y , ptA.x , ptA.y , r );
	    cxt.stroke ( );
    } ,

    // 绘制泳道池
    drawPool : function ( x , y , w , h , txt , icon , color ) {

	    var cxt = this.cxt;
	    this.h = h;
	    this.w = w;
	    this.x = x;
	    this.y = y;
	    cxt.strokeStyle = color || 'black';

	    this.rect ( x , y , w , h , color );

	    if ( txt ) {
		    cxt.font = "bold 12px 微软雅黑";
		    var ang = -90 * Math.PI / 180;
		    var centerX = x + 15;
		    var centerY = y + h / 2;
		    cxt.beginPath ( );

		    cxt.translate ( centerX , centerY );
		    cxt.rotate ( ang );

		    cxt.fillStyle = color;
		    cxt.strokeStyle = color;
		    cxt.fillText ( txt , 0 , 0 ); // 绘制文字 这里的0和0 搞了我好久

		    cxt.rotate ( -ang );
		    cxt.translate ( -centerX , -centerY );

		    cxt.closePath ( );
		    cxt.stroke ( );
	    }
    } ,
    // 绘制矩形
    rect : function ( x , y , w , h , color ) {

	    var cxt = this.cxt;
	    cxt.lineWidth = 1;
	    cxt.strokeStyle = color || 'black';
	    // 描边矩形
	    cxt.strokeRect ( x , y , w , h );

	    // $ ( this.canvas ).drawRect ( {
	    // // layer: true,
	    // strokeStyle : color ,
	    // strokeWidth : 1 ,
	    // cornerRadius : 0 ,
	    // x : x + w / 2 ,
	    // y : y + h / 2 ,
	    // width : w ,
	    // height : h ,
	    // } );
    } ,
    // 并行网关
    drawParallelGateway : function ( x , y , txt , color ) {

	    var cxt = this.cxt;
	    var l = 20;
	    x = x + l;
	    y = y + l;
	    this.drawGateWay ( x , y , l , txt , color );
	    this.drawJia ( x , y , l / 2 , color );
    } ,
    // 并行条件网关
    drawInclusiveGateway : function ( x , y , txt , color ) {

	    var cxt = this.cxt;
	    var l = 20;
	    x = x + l;
	    y = y + l;
	    this.drawGateWay ( x , y , l , txt , color );
	    x = x - l / 2;
	    y = y - l / 2;
	    this.drawCircle ( x , y , l , l , color , 2 );
    } ,
    // 分支网关
    drawExclusiveGateway : function ( x , y , txt , color ) {

	    var cxt = this.cxt;
	    var l = 20;
	    x = x + l;
	    y = y + l;
	    this.drawGateWay ( x , y , l , txt , color );
	    this.drawXX ( x , y , l / 2 - 2 , color );
    } ,
    // 绘制正菱形(也就是网关的外框)
    drawGateWay : function ( x , y , l , txt , color ) {

	    var cxt = this.cxt;
	    cxt.beginPath ( );
	    cxt.moveTo ( x , y + l );
	    cxt.lineTo ( x - l , y );
	    cxt.lineTo ( x , y - l );
	    cxt.lineTo ( x + l , y );
	    cxt.closePath ( );
	    cxt.fillStyle = color;
	    cxt.strokeStyle = color;
	    if ( txt ) {
		    cxt.font = "bold 12px 微软雅黑";
		    // 绘制文字
		    cxt.fillText ( txt , x + l / 2 , y + l , 100 );
	    }
	    cxt.stroke ( );
	    x = x;
	    y = y;
    } ,

    /**
     * 绘制子流程
     */
    drawSubProcess : function ( x , y , w , h , txt , color ) {

	    var cxt = this.cxt;
	    this.h = h;
	    this.w = w;
	    this.x = x;
	    this.y = y;
	    cxt.strokeStyle = color || 'black';
	    cxt.fillStyle = color;
	    if ( txt ) {
		    // cxt.font = "normal 14px 微软雅黑";
		    cxt.font = "bold 12px 微软雅黑";
		    cxt.fillText ( txt , x + 10 , y + 20 , 100 );
	    }
	    var rect = new this.Rect ( x , y , this.w , this.h );
	    this.drawRoundedRect ( rect , color , 5 );
    } ,
    /**
     * 绘制外部子流程
     */
    drawCallActivity : function ( x , y , w , h , txt , icon , color ) {

	    var cxt = this.cxt;
	    cxt.fillStyle = color;
	    this.drawSubProcess ( x , y , w , h , txt , color );
	    // 画一个加号
	    cxt.font = "bold 18px FontAwesome";
	    var icon = document.getElementById ( "plusIcon" ).textContent
	    cxt.fillText ( icon , x + w / 2 - 8 , y + h - 5 , 100 );
    } ,
    // 绘制注释节点
    drawTextAnnotation : function ( x , y , w , h , txt , color ) {

	    var cxt = this.cxt;
	    var arrow = new Arrow ( x , y , x , y + h );
	    arrow.setColor ( color );
	    arrow.draw ( cxt );

	    arrow = new Arrow ( x , y , x + 20 , y );
	    arrow.setColor ( color );
	    arrow.draw ( cxt );

	    arrow = new Arrow ( x , y + h , x + 20 , y + h );
	    arrow.setColor ( color );
	    arrow.draw ( cxt );

	    if ( txt ) {
		    cxt.font = "bold 12px 微软雅黑";
		    cxt.fillText ( txt , x + 10 , y + h / 2 , 100 );
	    }
    } ,
    // 绘制动态任务节点
    drawDynamicNode : function ( x , y , w , h , txt , icon , color , dynamicTaskCount ) {

	    this.drawTaskNode ( x , y , w , h , "" , "" , color );//
	    var offset = 3;
	    this.drawTaskNode ( x + offset , y + offset , w , h , "" , "" , color );//
	    this.drawTaskNode ( x + 2 * offset , y + 2 * offset , w , h , txt , dynamicTaskCount || 'dynamicTaskIcon' , color );//
    } ,
    // 画X (分支网关中间的叉叉)
    drawXX : function ( x , y , w , color ) {

	    var cxt = this.cxt;
	    var startX = x - w;
	    var startY = y - w;
	    var endX = x + w;
	    var endY = y + w;

	    var arrow = new Arrow ( startX , startY , endX , endY );
	    arrow.setColor ( color );
	    arrow.draw ( cxt );

	    var arrow = new Arrow ( startX , endY , endX , startY );
	    arrow.setColor ( color );
	    arrow.draw ( cxt );
    } ,
    // 画+ (同步网关中间的加号)
    drawJia : function ( x , y , w , color ) {

	    var cxt = this.cxt;

	    var arrow = new Arrow ( x - w , y , x + w , y );
	    arrow.setColor ( color );
	    arrow.draw ( cxt );

	    var arrow = new Arrow ( x , y - w , x , y + w );
	    arrow.setColor ( color );
	    arrow.draw ( cxt );
    } ,
    hasDrawedLineId : {
    // 记录已经画过的线,避免重复
    } ,
    // 绘制线条上面的文字
    drawLineTxt : function ( xoffset , yoffset , flowElement ) {

	    var cxt = this.cxt;
	    if ( !flowElement ) {
		    return;
	    }
	    var outgoingFlows = flowElement.outgoingFlows || [ ];
	    var incomingFlows = flowElement.incomingFlows || [ ];
	    outgoingFlows.pushAll ( incomingFlows );
	    for ( var k = 0 ; k < outgoingFlows.length ; k++ ) {
		    var outgoingFlow = outgoingFlows[k];
		    var name = outgoingFlow.name;
		    var id = outgoingFlow.id;
		    if ( !id || this.hasDrawedLineId[id] ) {
			    continue;
		    }
		    this.hasDrawedLineId[id] = true;
		    var flows = this.flowLocation[id];
		    var flowFrom = flows[0];
		    var flowTo = flows[1];
		    if ( name ) {
			    // 获取颜色
			    var color = this.flowColorJson[id] || this.Color.DEFAULT;
			    this.drawTxt ( flowFrom.x - xoffset , flowFrom.y - yoffset , flowTo.x - xoffset , flowTo.y - yoffset , name , color );
		    }
	    }
    } ,
    // 沿着线画文字
    drawTxt : function ( x1 , y1 , x2 , y2 , txt , color ) {

	    if ( txt == "连线1" ) {
	    }

	    var cxt = this.cxt;
	    var deg = this.getAngle ( x1 , y1 , x2 , y2 );
	    // 测量宽度
	    var txtWidth = cxt.measureText ( txt ).width / 2;

	    var ofy = Math.abs ( y2 - y1 ) / 2;
	    var ofx = Math.abs ( x2 - x1 ) / 2;

	    var startX = x1;
	    var startY = y1;
	    if ( deg == 0 ) {
		    startX = x1 + ofx - txtWidth / 2 - 5;
		    startY = y1 - 5;
	    } else if ( deg > 0 && deg < 90 ) {
		    deg = -deg;
		    startX = x1 + ofx - txtWidth / 2 - 10;
		    startY = y1 - ofy - 5;
	    } else if ( deg == 90 ) {
		    deg = -deg;
		    startX = x1 - 10;
		    startY = y1 - ofy + txtWidth / 2;
	    } else if ( deg > 90 && deg <= 180 ) {
		    deg = ( 180 - deg );
		    startX = x2 + ofx - txtWidth / 2 - 5;
		    startY = y2 + ofy - 10;
	    } else if ( deg > 180 && deg < 270 ) {
		    deg = - ( 180 + deg );
		    startX = x1 - ofx - txtWidth / 2;
		    startY = y1 + ofy + txtWidth / 2 - 10;
	    } else if ( deg == 270 ) {
		    deg = - ( 360 - deg );
		    startX = x2 - 5;
		    startY = y2 - ofy + txtWidth / 2 + 5;
	    } else {
		    deg = ( 360 - deg );
		    startX = x1 + ofx - txtWidth / 2 + 15;
		    startY = y1 + ofy - 5;
	    }
	    var ang = deg * Math.PI / 180;
	    var centerX = startX;
	    var centerY = startY;
	    // var ang = 90 * Math.PI/180;
	    cxt.beginPath ( );

	    cxt.translate ( centerX , centerY );
	    cxt.rotate ( ang );

	    cxt.fillStyle = color;
	    cxt.strokeStyle = color;
	    cxt.font = "normal 12px FontAwesome";
	    cxt.fillText ( txt , 0 , 0 ); // 绘制文字 这里的0和0 搞了我好久

	    cxt.rotate ( -ang );
	    cxt.translate ( -centerX , -centerY );

	    cxt.closePath ( );
	    cxt.stroke ( );
    } ,
    // 绘制任务节点(比如用户节点,服务节点)
    drawTaskNode : function ( x , y , w , h , txt , icon , color , revertIconReg ) {

	    var cxt = this.cxt;
	    this.flag = "step";
	    this.h = h;
	    this.w = w;
	    this.x = x;
	    this.y = y;
	    cxt.strokeStyle = color || 'black';
	    // cxt.measureText(this.icon.text); 测量宽度
	    if ( icon ) {
		    // cxt.font = "normal 14px 微软雅黑";
		    cxt.fillStyle = color;
		    var iconEl = document.getElementById ( icon );
		    if ( iconEl ) {
			    icon = document.getElementById ( icon ).textContent;
			    cxt.font = "bold 18px FontAwesome";
		    } else {
			    cxt.font = "bold 12px FontAwesome";
		    }

		    // 设置图标旋转角度
		    if ( revertIconReg ) {
			    var centerX = x + 25;
			    var centerY = y + 10;
			    if ( revertIconReg == -90 ) {
				    centerX = x + 20;
				    centerY = y + 25;
			    } else if ( revertIconReg == 90 ) {
				    centerX = x + 10;
				    centerY = y + 10;
			    }
			    var ang = revertIconReg * Math.PI / 180;
			    cxt.translate ( centerX , centerY );
			    cxt.rotate ( ang );
			    cxt.fillText ( icon , 0 , 0 ); // 绘制文字 这里的0和0 搞了我好久

			    cxt.rotate ( -ang );
			    cxt.translate ( -centerX , -centerY );
		    } else {
			    cxt.fillText ( icon , x + 5 , y + 19 , 100 ); // 绘制文字
		    }
	    }
	    if ( txt ) {
		    // cxt.font = "normal 14px 微软雅黑";
		    cxt.font = "bold 12px 微软雅黑";
		    
//		    txt = "我是测试的很长的内容, 记住了哈.凉风有性秋叶无边归我";
		    
		    // 处理文字换行
			var chr = txt.split("");
			var temp = "";				
			var row = [];
	    	for(var a = 0; a < chr.length; a++){
	    		if( cxt.measureText(temp).width < w-20 ){}
	    		else{
	    			row.push(temp);
	    			temp = "";
	    		}
	    		temp += chr[a];
	    	}
	    	
	    	row.push(temp);
	    	
	    	if(row.length>=2){
		    	var yoff = 10;
		    	if(row.length==2){
		    		yoff = 3;
		    	}
		    	if(row.length>=3){
		    		yoff = 0;
		    	}
	    		for(var b = 0; b < row.length; b++){
				    var txtWidth = ( w - cxt.measureText ( row[b] ).width ) / 2;
		    		cxt.fillText(row[b],x + txtWidth,y + h / 2 + yoff + (b)*20 , 100);
		    	}
	    	}else{
			    cxt.font = "bold 12px 微软雅黑";
			    var txtWidth = ( w - cxt.measureText ( txt ).width ) / 2;
			    cxt.fillText ( txt , x + txtWidth , y + h / 2 + 10 , 100 );
	    	}
	    	
	    	
		    
	    }
	    var rect = new this.Rect ( x , y , this.w , this.h );
	    this.drawRoundedRect ( rect , color , 5 );
    } ,
    // 获取两个点与平行线的角度 0~360
    getAngle : function ( x1 , y1 , x2 , y2 ) {

	    var cxt = this.cxt;
	    var x = x1 - x2 , y = y1 - y2;
	    if ( !x && !y ) {
		    return 0;
	    }
	    // var angle = (180+Math.abs(Math.atan2(y, x)) / Math.PI ) % 360
	    var angle = Math.atan2 ( y2 - y1 , x2 - x1 );
	    angle = -angle * 180 / Math.PI;
	    if ( angle < 0 ) {
		    angle = angle + 360;
	    }
	    return angle;
    },
    // 获取两个矩形之间的连线
    getPointFromTwoBox : function(startNode,endNode){
    	
    	var startPointCenter = {
    			x : startNode.x+startNode.width/2,
    			y : startNode.y+startNode.height/2,
    	}
    	
    	var endPointCenter = {
    			x : endNode.x+endNode.width/2,
    			y : endNode.y+endNode.height/2,
    	}
    	
    	
    	var len = 20;
    	// 求这两个点之间的角度
    	var ang = this.getAngle(startPointCenter.x,startPointCenter.y,endPointCenter.x,endPointCenter.y);
    	if(ang>=0 && ang<90){
        	var xof = Math.abs(startNode.x-endNode.x);
        	var yof = Math.abs(startNode.y-endNode.y);
    		// 判断两个矩形的y坐标的距离是否超过了60
    		if(yof>endNode.height+30){
    			len = (yof-endNode.height)/2;
        		var p1 = {x:startNode.x + startNode.width/2,y:startNode.y};
        		var p2 = {x:startNode.x + startNode.width/2,y:startNode.y - len};
        		var p3 = {x:endNode.x + endNode.width/2,y:startNode.y - len};
        		var p4 = {x:endNode.x + endNode.width/2,y:endNode.y+endNode.height};
        		return [p1,p2,p3,p4];
    		}
    		var p1 = {x:startNode.x + startNode.width/2,y:startNode.y};
    		var p2 = {x:startNode.x + startNode.width/2,y:endNode.y - len};
    		var p3 = {x:endNode.x + endNode.width/2,y:endNode.y - len};
    		var p4 = {x:endNode.x + endNode.width/2,y:endNode.y};
    		return [p1,p2,p3,p4];
    	}
    	if(ang==90 || ang==270){
    		var p1 = {x:startNode.x + startNode.width,y:startNode.y + startNode.height/2};
    		var p2 = {x:startNode.x + startNode.width + len,y:startNode.y + startNode.height/2};
    		var p3 = {x:endNode.x + endNode.width + len,y:endNode.y + endNode.height/2};
    		var p4 = {x:endNode.x + endNode.width,y:endNode.y + endNode.height/2};
    		return [p1,p2,p3,p4];
    	}
    	
    	if(ang>90 && ang<=180){
        	var xof = Math.abs(startNode.x-endNode.x);
        	var yof = Math.abs(startNode.y-endNode.y);
    		// 判断两个矩形的y坐标的距离是否超过了60
    		if(yof>endNode.height){
    			len = yof/2;
        		var p1 = {x:startNode.x + startNode.width/2,y:startNode.y};
        		var p2 = {x:startNode.x + startNode.width/2,y:endNode.y + endNode.height/2};
        		var p3 = {x:endNode.x + endNode.width,y:endNode.y + endNode.height/2};
        		var p4 = null;
        		return [p1,p2,p3];
    		}
    		
    		var p1 = {x:startNode.x + startNode.width/2,y:startNode.y};
    		var p2 = {x:startNode.x + startNode.width/2,y:endNode.y - len};
    		var p3 = {x:endNode.x + endNode.width/2,y:endNode.y - len};
    		var p4 = {x:endNode.x + endNode.width/2,y:endNode.y};
    		return [p1,p2,p3,p4];
    	}
    	
    	if(ang>180 && ang<270){
        	var xof = Math.abs(startNode.x-endNode.x);
        	var yof = Math.abs(startNode.y-endNode.y);
    		// 判断两个矩形的y坐标的距离是否超过了60
    		if(yof>startNode.height){
    			len = (yof-startNode.height)/2;
        		var p1 = {x:startNode.x + startNode.width/2,y:startNode.y + startNode.height};
        		var p2 = {x:startNode.x + startNode.width/2,y:startNode.y + startNode.height + len};
        		
        		var p3 = {x:endNode.x + endNode.width/2,y:startNode.y + startNode.height + len};
        		var p4 = {x:endNode.x + endNode.width/2,y:endNode.y};
        		return [p1,p2,p3,p4];
    		}
    		
    		var p1 = {x:startNode.x + startNode.width/2,y:startNode.y + startNode.height};
    		var p2 = {x:startNode.x + startNode.width/2,y:endNode.y + endNode.height + len};
    		var p3 = {x:endNode.x + endNode.width/2,y:endNode.y + endNode.height + len};
    		var p4 = {x:endNode.x + endNode.width/2,y:endNode.y + endNode.height};
    		return [p1,p2,p3,p4];
    	}

    	if(ang>270 && ang<=360){

        	var xof = Math.abs(startNode.x-endNode.x);
        	var yof = Math.abs(startNode.y+startNode.height-endNode.y);
    		// 判断两个矩形的y坐标的距离是否超过了60
    		if(yof>40){
    			len = yof/2;
        		var p1 = {x:startNode.x + startNode.width/2,y:startNode.y + startNode.height};
        		var p2 = {x:startNode.x + startNode.width/2,y:endNode.y - len};
        		var p3 = {x:endNode.x + endNode.width/2,y:endNode.y - len};
        		var p4 = {x:endNode.x + endNode.width/2,y:endNode.y};
        		return [p1,p2,p3,p4];
    		}
    		
    		var p1 = {x:startNode.x + startNode.width/2,y:startNode.y + startNode.height};
    		var p2 = {x:startNode.x + startNode.width/2,y:endNode.y + endNode.height + len};
    		var p3 = {x:endNode.x + endNode.width/2,y:endNode.y + endNode.height + len};
    		var p4 = {x:endNode.x + endNode.width/2,y:endNode.y + endNode.height};
    		return [p1,p2,p3,p4];
    	}
    },
    // 获取两组坐标之间最近的点.
    getMinDistancePoint : function(startsP, endsP) {
    	var minW = 1000000;
    	var temps = "";
    	var tempe = "";

    	for(var ii = 0 ; ii < startsP.length ; ii++){
    		var p = startsP[ii];
    		
    		for(var i = 0 ; i < endsP.length ; i++){
        		var tp = endsP[i];
        		var oldMin = minW;
        		minW = Math.min(minW,Math.sqrt(Math.pow(p.x - tp.x, 2) + Math.pow(p.y - tp.y, 2)));
        		if(minW < oldMin){
        			temps = p;
        			tempe = tp;
        		}
        	}
    	}
    	
        return {
        	s:temps,
        	e:tempe,
        };
    }
};